home *** CD-ROM | disk | FTP | other *** search
/ MacGames Sampler / PHT MacGames Bundle.iso / MacSource Folder / Samples from the CD / C and C++ / MIDI lib / MIDI.Txt < prev    next >
Text File  |  1994-04-20  |  28KB  |  861 lines

  1. ; Low Level MIDI routines with time-stamping
  2. ; Written by Kirk Austin 5/17/87
  3. ; This code is in the public domain and is absolutely free
  4. ; Note: Be sure and turn off range checking in LS Pascal
  5. ;          to prevent a crash.
  6.  
  7. ; Serial Chip equates
  8. SCCRd        EQU $1D8
  9. SCCWr        EQU $1DC
  10. aData        EQU 6
  11. aCtl            EQU 2
  12. bData        EQU 4
  13. bCtl            EQU 0
  14. TBE            EQU 2
  15.  
  16. ; Interrupt vector equates
  17. Lvl1DT        EQU $192
  18. Lvl2DT        EQU $1B2
  19. RxIntOffsetA    EQU 24
  20. TxIntOffsetA    EQU 16
  21. SpecRecCondA    EQU 28
  22. RxIntOffsetB    EQU 8
  23. TxIntOffsetB    EQU 0
  24. SpecRecCondB    EQU 12
  25.  
  26. ; 6522 equates
  27. VIA            EQU $1D4
  28. vT1C        EQU $800
  29. vT1CH        EQU $A00
  30. vT1L        EQU $C00
  31. vACR        EQU $1600
  32. vIER        EQU $1C00
  33.  
  34. ; XDEF all routines that need to be accessed externally
  35.  
  36.             XDEF        InitSCCA
  37.             XDEF        InitSCCB
  38.             XDEF        TxMIDIA
  39.             XDEF        TxMIDIB
  40.             XDEF        RxMIDIA
  41.             XDEF        RxMIDIB
  42.             XDEF        ResetSCCA
  43.             XDEF        ResetSCCB
  44.             XDEF        InitTimer
  45.             XDEF        LoadTimer
  46.             XDEF        StartCounter
  47.             XDEF        GetCounter
  48.             XDEF        QuitTimer
  49.             
  50. ; These are the routines for the Modem Port
  51.  
  52. ;     PROCEDURE InitSCCA;
  53. ; Call this routine at the beginning of your application if
  54. ; using the modem port for MIDI information transfers.
  55.  
  56. InitSCCA    
  57.             MOVE        SR,-(SP)        ; Save interrupts
  58.             MOVEM.L        D0/A0-A2,-(SP)    ; Save registers
  59.             ORI            #$0300,SR        ; Disable interrupts
  60.             
  61.             MOVE.L        SCCRd,A1        ; Get base Read address
  62.             ADD            #aCtl,A1            ; Add offset for control
  63.             MOVE.B        (A1),D0            ; Dummy read
  64.             MOVE.L        (SP),(SP)        ; Delay
  65.             MOVE.L        SCCWr,A0        ; Get base Write address
  66.             ADD            #aCtl,A0            ; Add offset for control
  67.             MOVE.B        #9,(A0)            ;  pointer for SCC reg 9
  68.             MOVE.L        (SP),(SP)        ; Delay
  69.             MOVE.B        #%10000000,(A0)    ; Reset channel
  70.             MOVE.L        (SP),(SP)        ; Delay
  71.             BSR            InitSCCChan        ; branch to common init routine
  72.             
  73. ; set up interrupt vectors
  74.  
  75.             MOVE.L        #Lvl2DT,A0        ; get dispatch table ptr
  76.             MOVE        #RxIntOffsetA,D0    ; get offset to Rx vector
  77.             LEA            PRxIntHandA,A1    ; point to previous vector storage
  78.             MOVE.L        0(A0,D0),(A1)    ; save previous interrupt vector
  79.             LEA            RxIntHandA,A1    ; set Rx vector
  80.             MOVE.L        A1,0(A0,D0)
  81.             MOVE        #TxIntOffsetA,D0    ; get offset to Tx vector
  82.             LEA            PTxIntHandA,A1    ; point to previous vector storage
  83.             MOVE.L        0(A0,D0),A1        ; save previous interrupt vector
  84.             LEA            TxIntHandA,A1    ; set Tx vector
  85.             MOVE.L        A1,0(A0,D0)
  86.             MOVE        #SpecRecCondA,D0    ; offset to Special vector
  87.             LEA            StubA,A1
  88.             MOVE.L        A1,0(A0,D0)
  89.             
  90. ; initialize the flags & pointers
  91.  
  92.             LEA            RxByteInA,A2        ; get the address
  93.             CLR            (A2)
  94.             LEA            RxByteOutA,A2    ; get the address
  95.             CLR            (A2)
  96.             LEA            RxQEmptyA,A2    ; get the address
  97.             MOVE        #$FFFF,(A2)
  98.             LEA            TxByteInA,A2        ; get the address
  99.             CLR            (A2)
  100.             LEA            TxQEmptyA,A2    ; get the address
  101.             MOVE        #$FFFF,(A2)
  102.             
  103.             MOVEM.L        (SP)+,D0/A0-A2    ; Restore registers
  104.             MOVE        (SP)+,SR        ; Restore interrupts
  105.             RTS                            ; and return
  106.             
  107. ; This is the common initialization routine for both channels
  108.  
  109. InitSCCChan
  110.             MOVE.B        #4,(A0)            ; pointer for SCC reg 4
  111.             MOVE.L        (SP),(SP)        ; Delay
  112.             MOVE.B        #%10000100,(A0)    ; 32x clock, 1 stop bit
  113.             MOVE.L        (SP),(SP)        ; Delay
  114.             MOVE.B        #1,(A0)            ; pointer for SCC reg 1
  115.             MOVE.L        (SP),(SP)        ; Delay
  116.             MOVE.B        #%00000000,(A0)    ; No W/Req
  117.             MOVE.L        (SP),(SP)        ; Delay
  118.             MOVE.B        #3,(A0)            ; pointer for SCC reg 3
  119.             MOVE.L        (SP),(SP)        ; Delay
  120.             MOVE.B        #%00000000,(A0)    ; Turn off Rx
  121.             MOVE.L        (SP),(SP)        ; Delay
  122.             MOVE.B        #5,(A0)            ; pointer for SCC reg 5
  123.             MOVE.L        (SP),(SP)        ; Delay
  124.             MOVE.B        #%00000000,(A0)    ; Turn off Tx
  125.             MOVE.L        (SP),(SP)        ; Delay
  126.             MOVE.B        #11,(A0)        ; pointer for SCC reg 11
  127.             MOVE.L        (SP),(SP)        ; Delay
  128.             MOVE.B        #%00101000,(A0)    ; Make TRxC clock source
  129.             MOVE.L        (SP),(SP)        ; Delay
  130.             MOVE.B        #14,(A0)        ; pointer for SCC reg 14
  131.             MOVE.L        (SP),(SP)        ; Delay
  132.             MOVE.B        #%00000000,(A0)    ; Disable BRGen
  133.             MOVE.L        (SP),(SP)        ; Delay
  134.             MOVE.B        #3,(A0)            ; pointer for SCC reg 3
  135.             MOVE.L        (SP),(SP)        ; Delay
  136.             MOVE.B        #%11000001,(A0)    ; Enable Rx
  137.             MOVE.L        (SP),(SP)        ; Delay
  138.             MOVE.B        #5,(A0)            ;  pointer for SCC reg 5
  139.             MOVE.L        (SP),(SP)        ; Delay
  140.             MOVE.B        #%01101010,(A0)    ; Enable Tx and drivers
  141.             MOVE.L        (SP),(SP)        ; Delay
  142.             MOVE.B        #15,(A0)        ; pointer for SCC reg 15
  143.             MOVE.L        (SP),(SP)        ; Delay
  144.             MOVE.B        #%00001000,(A0)    ; Enable DCD int for mouse
  145.             MOVE.L        (SP),(SP)        ; Delay
  146.             MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  147.             MOVE.L        (SP),(SP)        ; Delay
  148.             MOVE.B        #%00010000,(A0)    ; Reset EXT/STATUS
  149.             MOVE.L        (SP),(SP)        ; Delay
  150.             MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  151.             MOVE.L        (SP),(SP)        ; Delay
  152.             MOVE.B        #%00010000,(A0)    ; Reset EXT/STATUS again
  153.             MOVE.L        (SP),(SP)        ; Delay
  154.             MOVE.B        #1,(A0)            ; pointer for SCC reg 1
  155.             MOVE.L        (SP),(SP)        ; Delay
  156.             MOVE.B        #%00010011,(A0)    ; Enable interrupts
  157.             MOVE.L        (SP),(SP)        ; Delay
  158.             MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  159.             MOVE.L        (SP),(SP)        ; Delay
  160.             MOVE.B        #%00001010,(A0)    ; Set master int enable
  161.             MOVE.L        (SP),(SP)        ; Delay
  162.             RTS
  163.             
  164. ;      PROCEDURE TxMIDIA (TheData : integer);
  165. ; This is the routine to transmit a MIDI byte of data
  166. ; through the Modem Port.  To use this routine place
  167. ; the byte to be transmitted as the lower 8 bits
  168. ; of a word on the stack, then call TxMIDIA.
  169.  
  170. TxMIDIA
  171.             LINK        A6,#0            ; set frame pointer
  172.             MOVE        SR,-(SP)        ; Save interrupts
  173.             MOVEM.L        D0/A0-A3,-(SP)    ; Save registers
  174.             ORI            #$0300,SR        ; Disable interrupts
  175.             
  176.             LEA            TxQEmptyA,A3    ; get the address
  177.             TST.B        (A3)            ; is TxQueue empty?
  178.             BNE            TxQEA            ; if so branch
  179.             LEA            TxByteInA,A3        ; get the address
  180.             MOVE        (A3),D0            ; if not add byte to queue
  181.             LEA            TxQueueA,A2        ; point to queue
  182.             MOVE.B        9(A6),0(A2,D0)    ; place byte in queue
  183.             ADDQ        #1,D0            ; update TxByteIn
  184.             CMP            #$100,D0
  185.             BNE            @1
  186.             MOVE        #0,D0
  187. @1            MOVE        D0,(A3)
  188.             BRA            TxExitA            ; and exit
  189.             
  190. TxQEA
  191.             MOVE.L        SCCRd,A0        ; get SCC Read Address
  192.             MOVE.L        SCCWr,A1        ; get SCC Write Address
  193.             MOVE        #aCtl,D0            ; get index for Ctl
  194.             BTST.B        #TBE,0(A0,D0)    ; transmit buffer empty?
  195.             BNE            FirstByteA        ; if so branch
  196.             LEA            TxByteInA,A3        ; get the address
  197.             MOVE        (A3),D0            ; if not add to queue
  198.             LEA            TxQueueA,A2        ; point to queue
  199.             MOVE.B        9(A6),0(A2,D0)    ; place byte in queue
  200.             ADDQ        #1,D0            ; update pointer
  201.             CMP            #$100,D0
  202.             BNE            @1
  203.             MOVE        #0,D0
  204. @1            MOVE        D0,(A3)
  205.             LEA            TxQEmptyA,A3    ; get the address
  206.             MOVE        #0,(A3)            ; reset queue empty flag
  207.             BRA            TxExitA            ; and exit
  208.             
  209. FirstByteA
  210.             MOVE        #aData,D0        ; get index to data
  211.             MOVE.L        (SP),(SP)        ; Delay
  212.             MOVE.B        9(A6),0(A1,D0)    ; write data to SCC
  213.             MOVE.L        (SP),(SP)        ; Delay
  214.             
  215. TxExitA
  216.             MOVEM.L        (SP)+,D0/A0-A3    ; Restore registers
  217.             MOVE        (SP)+,SR        ; Restore interrupts
  218.             UNLK        A6                ; release frame pointer
  219.             MOVE.L        (SP)+,A1        ; save return address
  220.             ADD.L        #2,SP            ; move past data word
  221.             MOVE.L        A1,-(SP)        ; put address back on stack
  222.             RTS                            ; and return
  223.             
  224. ;      Function RxMIDIA : LongInt;
  225. ; This routine gets a byte through the modem port.
  226. ; To use this routine treat it like a Pascal
  227. ; function.  Leave space on the stack for a longword
  228. ; of data before calling this routine.  If the data
  229. ; on the stack after
  230. ; the routine executes is 0 there was no MIDI data available.
  231. ; If it's non-0 the upper 3 bytes contain the counter
  232. ; value, the MIDI byte is the low byte.
  233.  
  234. RxMIDIA
  235.             LINK        A6,#0            ; set frame pointer
  236.             MOVE        SR,-(SP)        ; save interrupts
  237.             MOVEM.L        D0-D1/A0-A3,-(SP)    ; Save registers
  238.             ORI            #$0300,SR        ; disable interrupts
  239.             
  240.             LEA            RxQEmptyA,A3    ; get the address
  241.             TST.B        (A3)            ; any data available?
  242.             BEQ            @1                ; if so, branch
  243.             MOVE.L        #0,8(A6)        ; if not, return with 0
  244.             BRA            RxExitA
  245. @1            LEA            RxByteOutA,A3    ; get the address
  246.             MOVE        (A3),D0            ; get index to byte out
  247.             LEA            RxQueueA,A2        ; point to queue
  248.             MOVE.L        #0,D1            ; clear data register
  249.             MOVE.L        0(A2,D0),D1        ; get MIDI data
  250.             MOVE.L        D1,8(A6)        ; place on stack for return
  251.             ADDQ        #4,D0            ; update index
  252.             CMP            #$400,D0
  253.             BNE            @2
  254.             MOVE        #0,D0
  255. @2            LEA            RxByteOutA,A3    ; get the address
  256.             MOVE        D0,(A3)
  257.             LEA            RxByteInA,A3        ; get the address
  258.             MOVE        (A3),D1
  259.             CMP            D0,D1            ; is queue empty?
  260.             BNE            RxExitA            ; if not exit
  261.             LEA            RxQEmptyA,A3    ; get the address
  262.             MOVE        #$FFFF,(A3)        ; if empty, set flag
  263.             
  264. RxExitA
  265.             MOVEM.L        (SP)+,D0-D1/A0-A3    ; Restore registers
  266.             MOVE        (SP)+,SR        ; restore interrupts
  267.             UNLK        A6
  268.             RTS                            ; and return
  269.             
  270. ;  This is the interrupt routine for receiving through
  271. ; the modem port.  It places the counter value and the
  272. ; MIDI byte in a circular queue to be
  273. ; accessed later by the application.
  274. ; When the system gets this far, A0 contains the
  275. ; SCC base read Ctl address
  276. ; and A1 contains the SCC base write Ctl address
  277. ; for this channel.  The data addresses are offset by 4
  278. ; from the control addresses.
  279. ; D0-D3/A0-A3 are already preserved, so they may
  280. ; be used freely.
  281.  
  282. RxIntHandA
  283.             ORI            #$0300,SR        ; disable interrupts
  284.             
  285. @3            MOVE        #4,D0            ; get data offset
  286.             CLR.L        D1                ; prepare for data
  287.             MOVE.L        (SP),(SP)        ; Delay
  288.             MOVE.B        0(A0,D0),D1        ; read data from SCC
  289.             MOVE.L        (SP),(SP)        ; Delay
  290.             LEA            RxQueueA,A2        ; point to queue
  291.             LEA            RxByteInA,A3        ; get the address
  292.             MOVE        (A3),D0            ; get offset to next cell
  293.             LEA            Counter,A3        ; get the address
  294.             MOVE.L        (A3),D2            ; put counter value in D2
  295.             LSL.L        #8,D2            ; shift counter one byte
  296.             ADD.L        D2,D1            ; combine counter and data
  297.             MOVE.L        D1,0(A2,D0)        ; put longword in queue
  298.             LEA            RxQEmptyA,A3    ; get the address
  299.             MOVE        #0,(A3)            ; reset queue empty flag
  300.             ADDQ        #4,D0            ; update index
  301.             CMP            #$400,D0
  302.             BNE            @1
  303.             MOVE        #0,D0
  304. @1            LEA            RxByteInA,A3        ; get the address
  305.             MOVE        D0,(A3)
  306.             
  307. @2            BTST.B        #0,(A0)            ; is there more data?
  308.             BNE            @3                ; do it again if there is
  309.             
  310.             ANDI        #$F8FF,SR        ; enable interrupts
  311.             RTS                            ; and return
  312.             
  313. ; This is the interrupt routine for transmitting a byte
  314. ; through the modem port.  It checks to see if there
  315. ; is any data to send, and if there is it sends it to
  316. ; the SCC.  If there isn't it resets the TBE interrupt
  317. ; in the SCC and exits.
  318. ; When the system gets this far, A0 contains the SCC
  319. ; base read Ctl address and A1 contains the SCC base
  320. ; write Ctl address for this channel.
  321. ; The data addresses are offset by 4 from the control
  322. ; addresses. D0-D3/A0-A3 are already preserved, so
  323. ; they may be used freely.
  324.  
  325. TxIntHandA
  326.             ORI            #$0300,SR        ; disable interrupts
  327.             
  328.             LEA            TxQEmptyA,A3    ; get the address
  329.             TST.B        (A3)            ; Is queue empty?
  330.             BEQ            @1                ; if not branch
  331.             MOVE.B        #$28,(A1)        ; if so, reset TBE interrupt
  332.             MOVE.L        (SP),(SP)        ; Delay
  333.             BRA            TxIExitA            ; and exit
  334. @1            LEA            TxByteOutA,A3    ; get the address
  335.             MOVE        (A3),D0            ; get index to next data byte
  336.             LEA            TxQueueA,A2        ; point to queue
  337.             MOVE        #4,D1            ; get data offset
  338.             MOVE.B        0(A2,D0),0(A1,D1)    ; write data to SCC
  339.             MOVE.L        (SP),(SP)        ; Delay
  340.             ADDQ        #1,D0            ; update index
  341.             CMP            #$100,D0
  342.             BNE            @2
  343.             MOVE        #0,D0
  344. @2            LEA            TxByteOutA,A3    ; get the address
  345.             MOVE        D0,(A3)
  346.             LEA            TxByteInA,A3        ; get the address
  347.             MOVE        (A3),D1
  348.             CMP            D0,D1            ; is TxQueue empty?
  349.             BNE            TxIExitA            ; if not exit
  350.             LEA            TxQEmptyA,A3    ; get the address
  351.             MOVE        #$FFFF,(A3)        ; if empty set flag
  352.             
  353. TxIExitA
  354.             ANDI        #$F8FF,SR        ; enable interrupts
  355.             RTS                            ; and return
  356.             
  357. ;      PROCEDURE ResetSCCA;
  358. ; If you called InitSCCA at the beginning of your
  359. ; application this
  360. ; routine must be called when the application
  361. ; quits or the system will
  362. ; crash due to the interrupt handling pointers
  363. ; becoming invalid.
  364.  
  365. ResetSCCA
  366.             MOVEM.L        D0/A0-A1,-(SP)    ; save registers
  367.             MOVE        SR,-(SP)        ; Save interrupts
  368.             ORI            #$0300,SR        ; Disable interrupts
  369.             
  370.             MOVE.L        SCCWr,A0        ; Get base Write address
  371.             ADD            #aCtl,A0            ; Add offset for control
  372.             MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  373.             MOVE.L        (SP),(SP)        ; Delay
  374.             MOVE.B        #%10000000,(A0)    ; Reset channel
  375.             MOVE.L        (SP),(SP)        ; Delay
  376.             BSR            ResetSCCChan        ; branch to common reset routine
  377.             MOVE.L        #Lvl2DT,A0        ; get dispatch table pointer
  378.             MOVE        #RxIntOffsetA,D0    ; get offset to Rx vector
  379.             LEA            PRxIntHandA,A1    ; point to previous vector storage
  380.             MOVE.L        (A1),0(A0,D0)    ; restore previous int vector
  381.             MOVE        #TxIntOffsetA,D0    ; get offset to Tx vector
  382.             LEA            PTxIntHandA,A1    ; set Rx vector
  383.             MOVE.L        (A1),0(A0,D0)    ; restore previous int vector
  384.             
  385.             MOVE        (SP)+,SR        ; Restore interrupts
  386.             MOVEM.L        (SP)+,D0/A0-A1    ; restore registers
  387.             RTS                            ; and return
  388.  
  389. ; This is the common reset routine for both channels
  390.  
  391. ResetSCCChan
  392.             MOVE.B        #15,(A0)        ; pointer for SCC reg 15
  393.             MOVE.L        (SP),(SP)        ; Delay
  394.             MOVE.B        #%00001000,(A0)    ; Enable DCD int
  395.             MOVE.L        (SP),(SP)        ; Delay
  396.             MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  397.             MOVE.L        (SP),(SP)        ; Delay
  398.             MOVE.B        #%00010000,(A0)    ; Reset EXT/STATUS
  399.             MOVE.L        (SP),(SP)        ; Delay
  400.             MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  401.             MOVE.L        (SP),(SP)        ; Delay
  402.             MOVE.B        #%00010000,(A0)    ; Reset EXT/STATUS again
  403.             MOVE.L        (SP),(SP)        ; Delay
  404.             MOVE.B        #1,(A0)            ; pointer for SCC reg 1
  405.             MOVE.L        (SP),(SP)        ; Delay
  406.             MOVE.B        #%00000001,(A0)    ; Enable mouse interrupts
  407.             MOVE.L        (SP),(SP)        ; Delay
  408.             MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  409.             MOVE.L        (SP),(SP)        ; Delay
  410.             MOVE.B        #%00001010,(A0)    ; Set master int enable
  411.             MOVE.L        (SP),(SP)        ; Delay
  412.             RTS
  413.             
  414. TxQueueA        DCB.B        $100,0            ; this is the queue
  415. TxQEmptyA    DC            0                ; the queue empty flag
  416. TxByteInA    DC            0                ; index to next cell in
  417. TxByteOutA    DC            0                ; index to next cell out
  418. RxQueueA    DCB.B        $400,0            ; this is the queue
  419. RxQEmptyA    DC            0                ; the empty queue flag
  420. RxByteInA    DC            0                ; index to next cell in
  421. RxByteOutA    DC            0                ; index to next cell out
  422. PRxIntHandA    DC            0                ; Previous interrupt vector
  423. PTxIntHandA    DC            0                ; Previous interrupt vector
  424.  
  425. ; These are the routines for the Printer Port
  426.  
  427. ;     PROCEDURE InitSCCB;
  428. ; Call this routine at the beginning of your application if
  429. ; using the printer port for MIDI information transfers.
  430.  
  431. InitSCCB
  432.             MOVE        SR,-(SP)        ; Save interrupts
  433.             MOVEM.L        D0/A0-A2,-(SP)    ; Save registers
  434.             ORI            #$0300,SR        ; Disable interrupts
  435.             
  436.             MOVE.L        SCCRd,A1        ; Get base Read address
  437.             ADD            #bCtl,A1            ; Add offset for control
  438.             MOVE.B        (A1),D0            ; Dummy read
  439.             MOVE.L        (SP),(SP)        ; Delay
  440.             MOVE.L        SCCWr,A0        ; Get base Write address
  441.             ADD            #bCtl,A0            ; Add offset for control
  442.             MOVE.B        #9,(A0)            ;  pointer for SCC reg 9
  443.             MOVE.L        (SP),(SP)        ; Delay
  444.             MOVE.B        #%01000000,(A0)    ; Reset channel
  445.             MOVE.L        (SP),(SP)        ; Delay
  446.             BSR            InitSCCChan        ; branch to common init routine
  447.             
  448. ; set up interrupt vectors
  449.  
  450.             MOVE.L        #Lvl2DT,A0        ; get dispatch table ptr
  451.             MOVE        #RxIntOffsetB,D0    ; get offset to Rx vector
  452.             LEA            PRxIntHandB,A1    ; point to previous vector storage
  453.             MOVE.L        0(A0,D0),(A1)    ; save previous interrupt vector
  454.             LEA            RxIntHandB,A1    ; set Rx vector
  455.             MOVE.L        A1,0(A0,D0)
  456.             MOVE        #TxIntOffsetB,D0    ; get offset to Tx vector
  457.             LEA            PTxIntHandB,A1    ; point to previous vector storage
  458.             MOVE.L        0(A0,D0),A1        ; save previous interrupt vector
  459.             LEA            TxIntHandB,A1    ; set Tx vector
  460.             MOVE.L        A1,0(A0,D0)
  461.             MOVE        #SpecRecCondB,D0    ; offset to Special vector
  462.             LEA            StubB,A1
  463.             MOVE.L        A1,0(A0,D0)
  464.             
  465. ; initialize the flags & pointers
  466.  
  467.             LEA            RxByteInB,A2        ; get the address
  468.             CLR            (A2)
  469.             LEA            RxByteOutB,A2    ; get the address
  470.             CLR            (A2)
  471.             LEA            RxQEmptyB,A2    ; get the address
  472.             MOVE        #$FFFF,(A2)
  473.             LEA            TxByteInB,A2        ; get the address
  474.             CLR            (A2)
  475.             LEA            TxQEmptyB,A2    ; get the address
  476.             MOVE        #$FFFF,(A2)
  477.             
  478.             MOVEM.L        (SP)+,D0/A0-A2    ; Restore registers
  479.             MOVE        (SP)+,SR        ; Restore interrupts
  480.             RTS                            ; and return
  481.             
  482. ;      PROCEDURE TxMIDIB (TheData : integer);
  483. ; This is the routine to transmit a MIDI byte of data
  484. ; through the Printer Port.  To use this routine place
  485. ; the byte to be transmitted as the lower 8 bits
  486. ; of a word on the stack, then call TxMIDIB.
  487.  
  488. TxMIDIB
  489.             LINK        A6,#0            ; set frame pointer
  490.             MOVE        SR,-(SP)        ; Save interrupts
  491.             MOVEM.L        D0/A0-A3,-(SP)    ; Save registers
  492.             ORI            #$0300,SR        ; Disable interrupts
  493.             
  494.             LEA            TxQEmptyB,A3    ; get the address
  495.             TST.B        (A3)            ; is TxQueue empty?
  496.             BNE            TxQEB            ; if so branch
  497.             LEA            TxByteInB,A3        ; get the address
  498.             MOVE        (A3),D0            ; if not add byte to queue
  499.             LEA            TxQueueB,A2        ; point to queue
  500.             MOVE.B        9(A6),0(A2,D0)    ; place byte in queue
  501.             ADDQ        #1,D0            ; update TxByteIn
  502.             CMP            #$100,D0
  503.             BNE            @1
  504.             MOVE        #0,D0
  505. @1            MOVE        D0,(A3)
  506.             BRA            TxExitB            ; and exit
  507.             
  508. TxQEB
  509.             MOVE.L        SCCRd,A0        ; get SCC Read Address
  510.             MOVE.L        SCCWr,A1        ; get SCC Write Address
  511.             MOVE        #bCtl,D0            ; get index for Ctl
  512.             BTST.B        #TBE,0(A0,D0)    ; transmit buffer empty?
  513.             BNE            FirstByteB        ; if so branch
  514.             LEA            TxByteInB,A3        ; get the address
  515.             MOVE        (A3),D0            ; if not add to queue
  516.             LEA            TxQueueB,A2        ; point to queue
  517.             MOVE.B        9(A6),0(A2,D0)    ; place byte in queue
  518.             ADDQ        #1,D0            ; update pointer
  519.             CMP            #$100,D0
  520.             BNE            @1
  521.             MOVE        #0,D0
  522. @1            MOVE        D0,(A3)
  523.             LEA            TxQEmptyB,A3    ; get the address
  524.             MOVE        #0,(A3)            ; reset queue empty flag
  525.             BRA            TxExitB            ; and exit
  526.             
  527. FirstByteB
  528.             MOVE        #bData,D0        ; get index to data
  529.             MOVE.L        (SP),(SP)        ; Delay
  530.             MOVE.B        9(A6),0(A1,D0)    ; write data to SCC
  531.             MOVE.L        (SP),(SP)        ; Delay
  532.             
  533. TxExitB
  534.             MOVEM.L        (SP)+,D0/A0-A3    ; Restore registers
  535.             MOVE        (SP)+,SR        ; Restore interrupts
  536.             UNLK        A6                ; release frame pointer
  537.             MOVE.L        (SP)+,A1        ; save return address
  538.             ADD.L        #2,SP            ; move past data word
  539.             MOVE.L        A1,-(SP)        ; put address back on stack
  540.             RTS                            ; and return
  541.             
  542. ;      Function RxMIDIB : LongInt;
  543. ; This routine gets a byte through the printer port.
  544. ; To use this routine treat it like a Pascal
  545. ; function.  Leave space on the stack for a longword
  546. ; of data before calling this routine.  If the data
  547. ; on the stack after
  548. ; the routine executes is 0 there was no MIDI data available.
  549. ; If it's non-0 the upper 3 bytes contain the counter
  550. ; value, the MIDI byte is the low byte.
  551.  
  552. RxMIDIB
  553.             LINK        A6,#0            ; set frame pointer
  554.             MOVE        SR,-(SP)        ; save interrupts
  555.             MOVEM.L        D0-D1/A0-A3,-(SP)    ; Save registers
  556.             ORI            #$0300,SR        ; disable interrupts
  557.             
  558.             LEA            RxQEmptyB,A3    ; get the address
  559.             TST.B        (A3)            ; any data available?
  560.             BEQ            @1                ; if so, branch
  561.             MOVE.L        #0,8(A6)        ; if not, return with 0
  562.             BRA            RxExitB
  563. @1            LEA            RxByteOutB,A3    ; get the address
  564.             MOVE        (A3),D0            ; get index to byte out
  565.             LEA            RxQueueB,A2        ; point to queue
  566.             MOVE.L        #0,D1            ; clear data register
  567.             MOVE.L        0(A2,D0),D1        ; get MIDI data
  568.             MOVE.L        D1,8(A6)        ; place on stack for return
  569.             ADDQ        #4,D0            ; update index
  570.             CMP            #$400,D0
  571.             BNE            @2
  572.             MOVE        #0,D0
  573. @2            LEA            RxByteOutB,A3    ; get the address
  574.             MOVE        D0,(A3)
  575.             LEA            RxByteInB,A3        ; get the address
  576.             MOVE        (A3),D1
  577.             CMP            D0,D1            ; is queue empty?
  578.             BNE            RxExitB            ; if not exit
  579.             LEA            RxQEmptyB,A3    ; get the address
  580.             MOVE        #$FFFF,(A3)        ; if empty, set flag
  581.             
  582. RxExitB
  583.             MOVEM.L        (SP)+,D0-D1/A0-A3    ; Restore registers
  584.             MOVE        (SP)+,SR        ; restore interrupts
  585.             UNLK        A6
  586.             RTS                            ; and return
  587.             
  588. ;  This is the interrupt routine for receiving through
  589. ; the printer port.  It places the counter value and the
  590. ; MIDI byte in a circular queue to be
  591. ; accessed later by the application.
  592. ; When the system gets this far, A0 contains the
  593. ; SCC base read Ctl address
  594. ; and A1 contains the SCC base write Ctl address
  595. ; for this channel.  The data addresses are offset by 4
  596. ; from the control addresses.
  597. ; D0-D3/A0-A3 are already preserved, so they may
  598. ; be used freely.
  599.  
  600. RxIntHandB
  601.             ORI            #$0300,SR        ; disable interrupts
  602.             
  603. @3            MOVE        #4,D0            ; get data offset
  604.             CLR.L        D1                ; prepare for data
  605.             MOVE.L        (SP),(SP)        ; Delay
  606.             MOVE.B        0(A0,D0),D1        ; read data from SCC
  607.             MOVE.L        (SP),(SP)        ; Delay
  608.             LEA            RxQueueB,A2        ; point to queue
  609.             LEA            RxByteInB,A3        ; get the address
  610.             MOVE        (A3),D0            ; get offset to next cell
  611.             LEA            Counter,A3        ; get the address
  612.             MOVE.L        (A3),D2            ; put counter value in D2
  613.             LSL.L        #8,D2            ; shift counter one byte
  614.             ADD.L        D2,D1            ; combine counter and data
  615.             MOVE.L        D1,0(A2,D0)        ; put longword in queue
  616.             LEA            RxQEmptyB,A3    ; get the address
  617.             MOVE        #0,(A3)            ; reset queue empty flag
  618.             ADDQ        #4,D0            ; update index
  619.             CMP            #$400,D0
  620.             BNE            @1
  621.             MOVE        #0,D0
  622. @1            LEA            RxByteInB,A3        ; get the address
  623.             MOVE        D0,(A3)
  624.             
  625. @2            BTST.B        #0,(A0)            ; is there more data?
  626.             BNE            @3                ; do it again if there is
  627.             
  628.             ANDI        #$F8FF,SR        ; enable interrupts
  629.             RTS                            ; and return
  630.             
  631. ; This is the interrupt routine for transmitting a byte
  632. ; through the printer port.  It checks to see if there
  633. ; is any data to send, and if there is it sends it to
  634. ; the SCC.  If there isn't it resets the TBE interrupt
  635. ; in the SCC and exits.
  636. ; When the system gets this far, A0 contains the SCC
  637. ; base read Ctl address and A1 contains the SCC base
  638. ; write Ctl address for this channel.
  639. ; The data addresses are offset by 4 from the control
  640. ; addresses. D0-D3/A0-A3 are already preserved, so
  641. ; they may be used freely.
  642.  
  643. TxIntHandB
  644.             ORI            #$0300,SR        ; disable interrupts
  645.             
  646.             LEA            TxQEmptyB,A3    ; get the address
  647.             TST.B        (A3)            ; Is queue empty?
  648.             BEQ            @1                ; if not branch
  649.             MOVE.B        #$28,(A1)        ; if so, reset TBE interrupt
  650.             MOVE.L        (SP),(SP)        ; Delay
  651.             BRA            TxIExitB            ; and exit
  652. @1            LEA            TxByteOutB,A3    ; get the address
  653.             MOVE        (A3),D0            ; get index to next data byte
  654.             LEA            TxQueueB,A2        ; point to queue
  655.             MOVE        #4,D1            ; get data offset
  656.             MOVE.B        0(A2,D0),0(A1,D1)    ; write data to SCC
  657.             MOVE.L        (SP),(SP)        ; Delay
  658.             ADDQ        #1,D0            ; update index
  659.             CMP            #$100,D0
  660.             BNE            @2
  661.             MOVE        #0,D0
  662. @2            LEA            TxByteOutB,A3    ; get the address
  663.             MOVE        D0,(A3)
  664.             LEA            TxByteInB,A3        ; get the address
  665.             MOVE        (A3),D1
  666.             CMP            D0,D1            ; is TxQueue empty?
  667.             BNE            TxIExitB            ; if not exit
  668.             LEA            TxQEmptyB,A3    ; get the address
  669.             MOVE        #$FFFF,(A3)        ; if empty set flag
  670.             
  671. TxIExitB
  672.             ANDI        #$F8FF,SR        ; enable interrupts
  673.             RTS                            ; and return
  674.             
  675. ;      PROCEDURE ResetSCCB;
  676. ; If you called InitSCCB at the beginning of your
  677. ; application this
  678. ; routine must be called when the application
  679. ; quits or the system will
  680. ; crash due to the interrupt handling pointers
  681. ; becoming invalid.
  682.  
  683. ResetSCCB
  684.             MOVEM.L        D0/A0-A1,-(SP)    ; save registers
  685.             MOVE        SR,-(SP)        ; Save interrupts
  686.             ORI            #$0300,SR        ; Disable interrupts
  687.             
  688.             MOVE.L        SCCWr,A0        ; Get base Write address
  689.             ADD            #bCtl,A0            ; Add offset for control
  690.             MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  691.             MOVE.L        (SP),(SP)        ; Delay
  692.             MOVE.B        #%01000000,(A0)    ; Reset channel
  693.             MOVE.L        (SP),(SP)        ; Delay
  694.             BSR            ResetSCCChan        ; branch to common reset routine
  695.             MOVE.L        #Lvl2DT,A0        ; get dispatch table pointer
  696.             MOVE        #RxIntOffsetB,D0    ; get offset to Rx vector
  697.             LEA            PRxIntHandB,A1    ; point to previous vector storage
  698.             MOVE.L        (A1),0(A0,D0)    ; restore previous int vector
  699.             MOVE        #TxIntOffsetB,D0    ; get offset to Tx vector
  700.             LEA            PTxIntHandB,A1    ; set Rx vector
  701.             MOVE.L        (A1),0(A0,D0)    ; restore previous int vector
  702.             
  703.             MOVE        (SP)+,SR        ; Restore interrupts
  704.             MOVEM.L        (SP)+,D0/A0-A1    ; restore registers
  705.             RTS                            ; and return
  706.  
  707. TxQueueB    DCB.B        $100,0            ; this is the queue
  708. TxQEmptyB    DC            0                ; the queue empty flag
  709. TxByteInB    DC            0                ; index to next cell in
  710. TxByteOutB    DC            0                ; index to next cell out
  711. RxQueueB    DCB.B        $400,0            ; this is the queue
  712. RxQEmptyB    DC            0                ; the empty queue flag
  713. RxByteInB    DC            0                ; index to next cell in
  714. RxByteOutB    DC            0                ; index to next cell out
  715. PRxIntHandB    DC            0                ; Previous interrupt vector
  716. PTxIntHandB    DC            0                ; Previous interrupt vector
  717.  
  718. ; This is the space for a special condition interrupt
  719. ; routine. All I do here is reset the error flag in the SCC
  720. ; and return.  When the system gets this far, A0 contains
  721. ; the SCC base read Ctl address
  722. ; and A1 contains the SCC base write Ctl address
  723. ; for this channel.
  724. ; The data addresses are offset by 4 from the control
  725. ; addresses.  D0-D3/A0-A3 are already preserved, so
  726. ; they may be used freely.
  727.  
  728. StubA
  729.             ORI            #$0300,SR        ; Disable interrupts
  730.             MOVE.B        #%00110000,(A1)    ; Reset Error
  731.             MOVE.L        (SP),(SP)        ;  Delay
  732.             ANDI        #$F8FF,SR        ; Restore interrupts
  733.             RTS
  734.  
  735. ; This is the space for a special condition interrupt
  736. ; routine. All I do here is reset the error flag in the SCC
  737. ; and return.  When the system gets this far, A0 contains
  738. ; the SCC base read Ctl address
  739. ; and A1 contains the SCC base write Ctl address
  740. ; for this channel.
  741. ; The data addresses are offset by 4 from the control
  742. ; addresses.  D0-D3/A0-A3 are already preserved, so
  743. ; they may be used freely.
  744.  
  745. StubB
  746.             ORI            #$0300,SR        ; Disable interrupts
  747.             MOVE.B        #%00110000,(A1)    ; Reset Error
  748.             MOVE.L        (SP),(SP)        ;  Delay
  749.             ANDI        #$F8FF,SR        ; Restore interrupts
  750.             RTS
  751.  
  752. ; These are the routines for the counter you can use for
  753. ; time-stamping the incoming MIDI data.  This is useful
  754. ; for writing sequencer type applications.
  755. ; The time-stamping is done on an iterrupt level,
  756. ; is extremely accurate,
  757. ; and uses the VIA timer #1.  This means that you can't
  758. ; use any of the Sound Manager routines because they use
  759. ; timer #1 too. If you want to create a metronome click
  760. ; you have to write your own code that accesses
  761. ; the sound hardware directly without using timer #1.
  762. ; InitTimer and LoadTimer expect a word on the stack
  763. ; to load the timer.
  764. ; To increment the counter every millisecond, load the
  765. ; timer with decimal 782.  If you aren't going to use
  766. ; time-stamping you can ignore these routines.
  767.  
  768. ;      PROCEDURE InitTimer ( TimrValue : integer);
  769. ; Only call InitTimer once at the beginning
  770. ; of your application 1 millisecond is decimal 782.
  771.  
  772. InitTimer
  773.             LINK        A6,#0            ; set frame pointer
  774.             MOVEM.L        D0/A0-A1,-(SP)    
  775.             MOVE.L        #Lvl1DT,A0        ; Point to level 1 dispatch table
  776.             LEA            PrevIVC,A1        ; Point to interrupt vector storage
  777.             MOVE.L        24(A0),(A1)        ; save previous interrupt vector
  778.             LEA            CounterIntHand,A1    ; point to new interrupt handler
  779.             MOVE.L        A1,24(A0)        ; put it in the dispatch table
  780.             MOVE.L        VIA,A1            ; point to the 6522 chip
  781.             ORI.B        #$40,vACR(A1)    ; set the timer to freerun mode
  782.             MOVE.B        #$C0,vIER(A1)    ; Enable timer interrupts
  783.             MOVE        8(A6),D0        ; Get timer value
  784.             MOVE.B        D0,vT1L(A1)        ; set timer lo byte
  785.             LSR            #8, D0            ; shift to hi byte
  786.             MOVE.B        D0,vT1CH(A1)    ; set timer hi byte
  787.             MOVEM.L        (SP)+,D0/A0-A1
  788.             UNLK        A6
  789.             MOVE.L        (SP)+,A0        ; save return address
  790.             ADDQ        #2,SP            ; move past timer value
  791.             MOVE.L        A0,-(SP)        ; replace return address
  792.             RTS
  793.             
  794. ;      PROCEDURE LoadTimer (TimrValue : integer);
  795. ; Call LoadTimer whenever you want to change the timer value.
  796. ; 1 millisecond is decimal 782.
  797.  
  798. LoadTimer
  799.             LINK        A6,#0            ; set frame pointer
  800.             MOVEM.L        D0/A0-A1,-(SP)
  801.             MOVE.L        VIA,A1            ; point to the 6522 chip
  802.             MOVE        8(A6),D0        ; Get timer value
  803.             MOVE.B        D0,vT1L(A1)        ; set timer lo byte
  804.             LSR            #8,D0            ; shift to hi byte
  805.             MOVE.B        D0,vT1CH(A1)    ; set timer hi byte
  806.             MOVEM.L        (SP)+,D0/A0-A1
  807.             UNLK        A6
  808.             MOVE.L        (SP)+,A0        ; save return address
  809.             ADDQ        #2,SP            ; move past timer value
  810.             MOVE.L        A0,-(SP)        ; replace return address
  811.             RTS
  812.             
  813. ;      PROCEDURE StartCounter;
  814. ; StartCounter sets the counter value to 1
  815.  
  816. StartCounter
  817.             LEA            Counter,A0        ; point to the counter
  818.             MOVE.L        #1,(A0)            ; set it to 1
  819.             RTS
  820.             
  821. ;      FUNCTION GetCounter : LongInt;
  822. ; GetCounter returns a longword that is the value
  823. ; of the counter
  824.  
  825. GetCounter
  826.             MOVE.L        A0,-(SP)
  827.             LEA            Counter,A0        ; point to the counter
  828.             MOVE.L        (A0),8(SP)        ; return it as function result
  829.             MOVE.L        (SP)+,A0
  830.             RTS
  831.             
  832. ;      PROCEDURE QuitTimer;
  833. ; Call QuitTimer when your application is done or the system will crash.
  834.  
  835. QuitTimer
  836.             MOVEM.L        A0-A1,-(SP)
  837.             MOVE.L        VIA,A1            ; Disable 6522 interrupts
  838.             MOVE.B        #$40,vIER(A1)
  839.             LEA            PrevIVC,A1        ; Restore previous interrupt vector
  840.             MOVE.L        #Lvl1DT,A0
  841.             MOVE.L        (A1),24(A0)
  842.             MOVEM.L        (SP)+,A0-A1
  843.             RTS
  844.             
  845. ; This is the interrupt handler routine for the counter.
  846. ; When the system gets this far A1 contains the base
  847. ; address of the VIA.
  848. ; It also preserves D0-D3/A0-A3.
  849.  
  850. CounterIntHand
  851.             LEA            Counter,A0        ; point to the counter
  852.             ADDQ.L        #1,(A0)            ; Increment it
  853.             MOVE.B        vT1C(A1),D0        ; Clear interrupt flag on 6522
  854.             RTS
  855.             
  856. Counter        DC.L            1                ; The counter
  857. PrevIVC        DC.L            0                ; Previous interrupt vector
  858.  
  859.             END
  860.             
  861.